
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;

public class PLPatternIntersection extends PLPattern {

		public PLPatternIntersection() {
			super("Intersection");
		}
		
		
		/*
		 * Function to validate response and compute confidence for given set of query and response
		 * Returns the confidence with which query and response are parallel 
		 * if confidence is lower than 0.5 compute new better response shape
		 */
		
		public float ValidateResponse(PLShape query, PLShape response, PLOutShape outBetterMatch) {
			float confidence = 0;
		
			if(query.GetShapeId()==PLShape.ShapeIdT.kLine || response.GetShapeId()==PLShape.ShapeIdT.kLine){
				confidence =  confIntersection(query.GetLines(), response.GetLines());	
				
			}
			else{
				confidence = confIntersection(query, response);
			}
			
			
		
			// if the confidence is low, possible choose to return a new shape that better fits the pattern
			
			if (confidence >= 0.5){
				return confidence;
			} 
			else  // write the code to compute new shape
			{
			//angle divided by PI/2 is essentially confidence, so to compute new angle, find product of
			// new confidence and PI/2
				
			//float angle_new = (float) (0.5f*(Math.PI/2));	
			
			//how to compute new shape with the angle given?
				
			}		
			
			return confidence;		
		}
		
		/*
		 * Function to improve upon the response to query, new response is also passes and need
		 * to check which response is better
		 * right now implementation is just checking confidence value with both the response shapes 
		 * and return the shape with bigger confidence value
		 */
		
		public PLShape ImproveResponse(PLShape query, PLShape responseOrig, PLShape responseNew , PLOutShape outBetterMatch){
			float confOrig =0f;
			float confNew = 0f;
			
			confOrig = ValidateResponse(query, responseOrig, outBetterMatch);
			confNew = ValidateResponse(query, responseNew, outBetterMatch);
			
			if (confOrig >= confNew){
				return responseOrig;
			}
			else{
				return responseNew;
			}

		}
		
		
	public float BuildResponse(PLShape query, PLOutShape response){
	    
		float confidence=0;
		
		switch(query.GetShapeId()){
			
	    	case kLine:
	    		//return intersecting line
	    		PLLine line1 = (PLLine)query;
	    		Point startL = line1.GetStart();
	    		Point endL = line1.GetEnd();
	    		startL.x -=10;
	    		endL.x +=10;
	    		PLLine resL = new PLLine(startL,endL);
	    		response.SetShape(resL);
	    		confidence=1;
	    		break;
	    		
			case kTriangle: 
			    //return intersecting triangle
				PLIsTriangle tri = (PLIsTriangle)query;
				Point startT = tri.GetStart();
				Point endT = tri.GetEnd();
				float widthT = tri.GetWidth();
				startT.x -=20;
	    		endT.x +=20;
	    		PLIsTriangle resT = new PLIsTriangle(startT,endT,widthT);
	    		response.SetShape(resT);
	    		confidence=1;
	    		break;
				
			case kRect: 
				//return intersecting rectangle
				PLRect rect = (PLRect)query;
				Point startR = rect.GetStart();
				Point endR = rect.GetEnd();
				startR.x +=20;
	    		endR.x -=20;
	    		PLRect resR = new PLRect(startR,endR);
	    		response.SetShape(resR);
	    		confidence=1;
	    		break;
	    		
			case kEllipse:
				//return intersecting ellipse
				PLEllipse ell = (PLEllipse)query;
				Point startE = ell.GetStart();
				Point endE = ell.GetEnd();
				startE.x +=20;
	    		endE.x -=20;
	    		PLEllipse resE = new PLEllipse(startE,endE);
	    		response.SetShape(resE);
	    		confidence=1;
	    		break;
	    	}
	    	return confidence;
	    }
		
//Intersection of Lines
		public float confIntersection(PLLine line1, PLLine line2){
			float conf, d1,d2,d3,d4,d5,d6,d,D;
			boolean b1, b2;
			int H,W;
			Point intersection = new Point();
			Point start1 = line1.GetStart();
			Point end1 = line1.GetEnd();
			Point start2 = line2.GetStart();
			Point end2 = line2.GetEnd();
			
			PLDebug.Log("Inside confIntersection single line\n");
			if (line1.GetSlope()==line2.GetSlope()){
				conf = 0;
				PLDebug.Log("Lines are parallel\n");
				
			}
			else{
				intersection = pointOfIntersection(line1, line2);
				b1 = pointOnLine(line1, intersection);
				b2 = pointOnLine(line2, intersection);
				
				if (b1&&b2){//lines are intersecting
					conf = 1f;
					PLDebug.Log("Lines are pure intersecting\n");
				}
				else{ //lines will intersect if extended
				
					//find the distance between the lines and normalize it with the total dia of canvas
					
					if(b1){//point of intersection lies on line1
						d1 = distanceBtwPoints(intersection, start2);
						d2 = distanceBtwPoints(intersection, end2);						
						d = Math.min(d1, d2);						
					}
					else if(b2){//point of intersection lies on line2
						d1 = distanceBtwPoints(intersection, start1);
						d2 = distanceBtwPoints(intersection, end1);						
						d = Math.min(d1, d2);						
					}
					else{//point of intersection does not lie on any line
						d1 = distanceBtwPoints(intersection, start1);
						d2 = distanceBtwPoints(intersection, end1);
						d3 = distanceBtwPoints(intersection, start2);
						d4 = distanceBtwPoints(intersection, end2);
						
						d5 = Math.min(d1, d2);
						d6 = Math.min(d4, d5);
						
						d = Math.min(d5, d6);							
					}
					
					W = PLGuiInfo.Obj().GetWidth();
					H = PLGuiInfo.Obj().GetHeight();
					D = (float) Math.sqrt(H*H+W*W);					
					conf = 1-(d/D);
				}
								
			}	
			return conf;
		}
		
	    public Point pointOfIntersection(PLLine lineA, PLLine lineB){
	    	
			Point startA, endA, startB, endB, intersection;		
			float aM, bM, aB, bB, isX=0, isY=0;
			startA = lineA.GetStart();
			endA = lineA.GetEnd();
			startB = lineB.GetStart();
			endB = lineB.GetEnd();	    	
	    	
			  if((endA.x-startA.x)==0){
			    isX=startA.x;
			    bM=lineB.GetSlope();
			    bB=endB.y-bM*endB.x;
			    isY=bM*isX+bB;
			  }
			  else if((endB.x-startB.x)==0){
			    isX=startB.x;
			    aM=lineA.GetSlope();
			    aB=endA.y-aM*endA.x;
			    isY=aM*isX+aB;
			  }
			  else{
			    aM=lineA.GetSlope();
			    bM=lineB.GetSlope();
			    aB=endA.y-aM*endA.x;
			    bB=endB.y-bM*endB.x;
			    isX=Math.max(((bB-aB)/(aM-bM)),0);
			    isY=aM*isX+aB;
			  }
			  intersection = new Point();
			  intersection.x=(int)isX;
			  intersection.y=(int)isY;
			  
			  return intersection;
			}
	    
	    public boolean pointOnLine(PLLine line, Point P){
	    	float EPSILON = 0.001f;
	    	Point start;
	    	start = line.GetStart();

	    	   float a = line.GetSlope();
	    	   float b = start.y - a * start.x;
	    	   if ( Math.abs(P.y - (a*P.x+b))< EPSILON){
	    	       return true;
	    	   }
	    	   else {
	    		   return false;
	    	   }
	    	   
	    }
	    
	    public float distanceBtwPoints(Point p1, Point p2){
	    	int deltaX = Math.abs(p1.x - p2.x);
	    	int deltaY = Math.abs(p1.y - p2.y);
	    	float distance = (float) Math.sqrt((deltaX*deltaX)+(deltaY*deltaY));
	    	return distance;
	    }
	    
//Intersection of a line and set of lines(triangle, rectangle, ellipse - even if there is 1 intersection conf is 1
	    public float confIntersection(PLLine[] lines1, PLLine[] lines2){
			
			float confTotal = 0;
			int total = Math.min(lines1.length, lines2.length)*2; //A line can intersect at most 2 sides of a polygon
			
			for (int i=0;i<lines1.length;i++){
				for (int j=0;j<lines2.length;j++){				
					confTotal += confIntersection(lines1[i],lines2[j]) ;											
				}
			}					
			return confTotal/total;			
		}


		
//Intersection of non line shapes
	    public float confIntersection(PLShape shape1, PLShape shape2){
	    	float conf=0;
	    	Shape s1  = shape1.GetJavaShape();
	    	Shape s2  = shape2.GetJavaShape();
	
	  
	     	Rectangle2D r1 = s1.getBounds2D();
	    	Rectangle2D r2 = s2.getBounds2D();
	    	Rectangle2D r3 = new Rectangle2D.Double();
	    	Rectangle2D.intersect(r1, r2, r3);
	        conf = area(r3) / Math.min(area(r1),area(r2));	    	    	
	    	return conf;
	    }
	    
	    private float area(Rectangle2D r) {
    	    return (float) (r.getWidth() * r.getHeight());
    	}	


}
